package hasher

import (
	"encoding/binary"
	"encoding/json"
	"hash"
	"hash/fnv"
	"reflect"
	"time"

	"gitee.com/gousing/helper/internal"
)

const (
	hashFnvOffset64 uint64 = 14695981039346656037
	hashFnvPrime64  uint64 = 1099511628211
)

// Fnv64Any 计算任意类型的Fnv64哈希值, 无法处理时返回 hashFnvOffset64
func Fnv64Any(v any) uint64 {
	if v == nil {
		return hashFnvOffset64
	}
	h := fnv.New64a()
	fav64AnyType(h, v)
	if h.Size() == 0 {
		return hashFnvOffset64
	}
	return h.Sum64()
}

func Fnv64String(v string) uint64 {
	if v == "" {
		return hashFnvOffset64
	}
	hash := hashFnvOffset64
	for i := 0; i < len(v); i++ {
		hash ^= uint64(v[i])
		hash *= hashFnvPrime64
	}
	return hash
}
func Fnv64Bytes(v []byte) uint64 {
	if len(v) == 0 {
		return hashFnvOffset64
	}
	h := fnv.New64a()
	fnv64Bytes(h, v)
	return h.Sum64()
}
func Fnv64Int(v int) uint64 {
	h := fnv.New64a()
	fnv64Int(h, v)
	return h.Sum64()
}
func Fnv64Int64(v int64) uint64 {
	h := fnv.New64a()
	fnv64Int64(h, v)
	return h.Sum64()
}
func Fnv64Int32(v int32) uint64 {
	h := fnv.New64a()
	fnv64Int32(h, v)
	return h.Sum64()
}
func Fnv64Int16(v int16) uint64 {
	h := fnv.New64a()
	fnv64Int16(h, v)
	return h.Sum64()
}
func Fnv64Int8(v int8) uint64 {
	h := fnv.New64a()
	fnv64Int8(h, v)
	return h.Sum64()
}
func Fnv64Uint(v uint) uint64 {
	h := fnv.New64a()
	fnv64Uint(h, v)
	return h.Sum64()
}
func Fnv64Uint64(v uint64) uint64 {
	h := fnv.New64a()
	fnv64Uint64(h, v)
	return h.Sum64()
}
func Fnv64Uint32(v uint32) uint64 {
	h := fnv.New64a()
	fnv64Uint32(h, v)
	return h.Sum64()
}
func Fnv64Uint16(v uint16) uint64 {
	h := fnv.New64a()
	fnv64Uint16(h, v)
	return h.Sum64()
}
func Fnv64Uint8(v uint8) uint64 {
	h := fnv.New64a()
	fnv64Uint8(h, v)
	return h.Sum64()
}
func Fnv64Float32(v float32) uint64 {
	h := fnv.New64a()
	fnv64Float32(h, v)
	return h.Sum64()
}
func Fnv64Float64(v float64) uint64 {
	h := fnv.New64a()
	fnv64Float64(h, v)
	return h.Sum64()
}
func Fnv64Bool(v bool) uint64 {
	h := fnv.New64a()
	fnv64Bool(h, v)
	return h.Sum64()
}
func Fnv64Complex64(v complex64) uint64 {
	var z complex64
	if v == z {
		return hashFnvOffset64
	}
	h := fnv.New64a()
	fnv64Complex64(h, v)
	return h.Sum64()
}
func Fnv64Complex128(v complex128) uint64 {
	var z complex128
	if v == z {
		return hashFnvOffset64
	}
	h := fnv.New64a()
	fnv64Complex128(h, v)
	return h.Sum64()
}
func Fnv64Time(t time.Time) uint64 {
	var z time.Time
	if t == z || t.IsZero() {
		return hashFnvOffset64
	}
	h := fnv.New64a()
	fnv64Int64(h, t.UnixNano())
	return h.Sum64()
}
func Fnv64Duration(t time.Duration) uint64 {
	if t == 0 {
		return hashFnvOffset64
	}
	h := fnv.New64a()
	fnv64Int64(h, t.Nanoseconds())
	return h.Sum64()
}
func Fnv64Uintptr(v uintptr) uint64 {
	if v == 0 {
		return hashFnvOffset64
	}
	h := fnv.New64a()
	fnv64Uintptr(h, v)
	return h.Sum64()
}

func fav64AnyType(h hash.Hash64, v interface{}) {
	if v == nil {
		// nil pointer
		return
	}
	switch val := v.(type) {
	case string:
		if len(val) == 0 {
			return
		}
		fnv64String(h, val)
	case []byte:
		if len(val) == 0 {
			return
		}
		fnv64Bytes(h, val)
	case bool:
		fnv64Bool(h, val)
	case int:
		fnv64Int(h, val)
	case int64:
		fnv64Int64(h, val)
	case int32: //Same Rune
		fnv64Int32(h, val)
	case int16:
		fnv64Int16(h, val)
	case int8:
		fnv64Int8(h, val)
	case uint:
		fnv64Uint(h, val)
	case uint64:
		fnv64Uint64(h, val)
	case uint32:
		fnv64Uint32(h, val)
	case uint16:
		fnv64Uint16(h, val)
	case uint8:
		fnv64Uint8(h, val)
	case float32:
		fnv64Float32(h, val)
	case float64:
		fnv64Float64(h, val)
	case complex64:
		if val == 0 {
			return
		}
		fnv64Complex64(h, val)
	case complex128:
		if val == 0 {
			return
		}
		fnv64Complex128(h, val)
	case time.Time:
		if val.IsZero() {
			return
		}
		fnv64Time(h, val)
	case time.Duration:
		if val == 0 {
			return
		}
		fnv64Duration(h, val)
	case uintptr:
		if val == 0 {
			return
		}
		fnv64Uintptr(h, val)
	case error:
		s := val.Error()
		if s == "" {
			return
		}
		fnv64String(h, val.Error())
	case *string:
		if len(*val) == 0 {
			return
		}
		fnv64String(h, *val)
	case *[]byte:
		if len(*val) == 0 {
			return
		}
		fnv64Bytes(h, *val)
	case *bool:
		fnv64Bool(h, *val)
	case *int:
		fnv64Int(h, *val)
	case *int64:
		fnv64Int64(h, *val)
	case *int32: //Same Rune
		fnv64Int32(h, *val)
	case *int16:
		fnv64Int16(h, *val)
	case *int8:
		fnv64Int8(h, *val)
	case *uint:
		fnv64Uint(h, *val)
	case *uint64:
		fnv64Uint64(h, *val)
	case *uint32:
		fnv64Uint32(h, *val)
	case *uint16:
		fnv64Uint16(h, *val)
	case *uint8:
		fnv64Uint8(h, *val)
	case *float32:
		fnv64Float32(h, *val)
	case *float64:
		fnv64Float64(h, *val)
	case *complex64:
		if *val == 0 {
			return
		}
		fnv64Complex64(h, *val)
	case *complex128:
		if *val == 0 {
			return
		}
		fnv64Complex128(h, *val)
	case *time.Time:
		if (*val).IsZero() {
			return
		}
		fnv64Time(h, *val)
	case *time.Duration:
		if *val == 0 {
			return
		}
		fnv64Duration(h, *val)
	case *uintptr:
		if *val == 0 {
			return
		}
		fnv64Uintptr(h, *val)
	case *error:
		s := (*val).Error()
		if s == "" {
			return
		}
		fnv64String(h, s)
	case struct{}, *struct{}:
		fnv64String(h, "struct{}")
	default:
		if b, ok := v.(interface{ Bytes() []byte }); ok {
			s := b.Bytes()
			if len(s) == 0 {
				return
			}
			_, _ = h.Write(s)
			return
		}
		if b, ok := v.(interface{ String() string }); ok {
			s := b.String()
			if s == "" {
				return
			}
			_, _ = h.Write(internal.StringToBytes(s))
			return
		}
		refVal := reflect.ValueOf(v)
		if !refVal.IsValid() {
			// IsValid reports whether v represents a value.
			// It returns false if v is the zero Value.
			// If [Value.IsValid] returns false, all other methods except String panic.
			// Most functions and methods never return an invalid Value.
			// If one does, its documentation states the conditions explicitly.
			return
		}
		for refVal.Kind() == reflect.Ptr {
			refVal = refVal.Elem()
			if !refVal.IsValid() {
				// 如果指针指向的值无效，直接返回
				return
			}
		}
		switch refVal.Kind() {
		case reflect.Chan:
			ptr := refVal.Pointer()
			var bytes [8]byte
			for i := 0; i < 8; i++ {
				bytes[i] = byte(ptr >> (uint(i) * 8))
			}
			_, _ = h.Write(bytes[:])
			return
		case reflect.String,
			reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64,
			reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64,
			reflect.Uintptr,
			reflect.Float32, reflect.Float64,
			reflect.Complex64, reflect.Complex128,
			reflect.Bool:
			fav64AnyType(h, refVal.Interface())
			return
		}
		if bytes, err := json.Marshal(refVal.Interface()); err == nil {
			_, _ = h.Write(bytes)
			return
		}
		// 法处理时返回 hashFnvOffset64
		return
	}
}
func fnv64String(h hash.Hash64, s string) {
	_, _ = h.Write(internal.StringToBytes(s))
}

func fnv64Bytes(h hash.Hash64, b []byte) {
	_, _ = h.Write(b)
}

func fnv64Bool(h hash.Hash64, b bool) {
	if b {
		_, _ = h.Write([]byte{1})
	} else {
		_, _ = h.Write([]byte{0})
	}
}
func fnv64Int(h hash.Hash64, n int) {
	if internal.Is64Bit {
		binary.Write(h, binary.BigEndian, int64(n))
	} else {
		binary.Write(h, binary.BigEndian, int32(n))
	}
}
func fnv64Int8(h hash.Hash64, n int8) {
	binary.Write(h, binary.BigEndian, n)
}
func fnv64Int16(h hash.Hash64, n int16) {
	binary.Write(h, binary.BigEndian, n)
}
func fnv64Int32(h hash.Hash64, n int32) {
	binary.Write(h, binary.BigEndian, n)
}
func fnv64Int64(h hash.Hash64, n int64) {
	binary.Write(h, binary.BigEndian, n)
}

func fnv64Float32(h hash.Hash64, n float32) {
	binary.Write(h, binary.BigEndian, n)
}

func fnv64Float64(h hash.Hash64, n float64) {
	binary.Write(h, binary.BigEndian, n)
}

func fnv64Uint(h hash.Hash64, n uint) {
	if internal.Is64Bit {
		n64 := uint64(n)
		_, _ = h.Write([]byte{byte(n64 >> 56), byte(n64 >> 48), byte(n64 >> 40), byte(n64 >> 32), byte(n64 >> 24), byte(n64 >> 16), byte(n64 >> 8), byte(n)})
	} else {
		n32 := uint32(n)
		_, _ = h.Write([]byte{byte(n32 >> 24), byte(n32 >> 16), byte(n32 >> 8), byte(n32)})
	}
}
func fnv64Uint8(h hash.Hash64, n uint8) {
	_, _ = h.Write([]byte{byte(n)})
}
func fnv64Uint16(h hash.Hash64, n uint16) {
	_, _ = h.Write([]byte{byte(n >> 8), byte(n)})
}
func fnv64Uint32(h hash.Hash64, n uint32) {
	_, _ = h.Write([]byte{byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n)})
}

func fnv64Uint64(h hash.Hash64, n uint64) {
	_, _ = h.Write([]byte{byte(n >> 56), byte(n >> 48), byte(n >> 40), byte(n >> 32), byte(n >> 24), byte(n >> 16), byte(n >> 8), byte(n)})
}
func fnv64Complex64(h hash.Hash64, n complex64) {
	binary.Write(h, binary.LittleEndian, real(n))
	binary.Write(h, binary.LittleEndian, imag(n))
}

func fnv64Complex128(h hash.Hash64, n complex128) {
	binary.Write(h, binary.LittleEndian, real(n))
	binary.Write(h, binary.LittleEndian, imag(n))
}
func fnv64Time(h hash.Hash64, t time.Time) {
	fnv64Int64(h, t.UnixNano())
}
func fnv64Duration(h hash.Hash64, t time.Duration) {
	fnv64Int64(h, t.Nanoseconds())
}
func fnv64Uintptr(h hash.Hash64, n uintptr) {
	if internal.Is64Bit {
		u64 := uint64(n)
		_, _ = h.Write([]byte{byte(u64 >> 56), byte(u64 >> 48), byte(u64 >> 40), byte(u64 >> 32), byte(u64 >> 24), byte(u64 >> 16), byte(u64 >> 8), byte(u64)})
	} else {
		u32 := uint32(n)
		_, _ = h.Write([]byte{byte(u32 >> 24), byte(u32 >> 16), byte(u32 >> 8), byte(u32)})
	}
}
